///////////////////////////////////////////////////////////////////////////////////////////////////
/// \file formic/fqmc/walker.h
///
/// \brief   header file for the walker class
///
///////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef FORMIC_FQMC_WALKER_HEADER
#define FORMIC_FQMC_WALKER_HEADER

//#include<cstdlib>
//#include<ctime>

#include<formic/input/base.h>
//#include<formic/output/output.h>

namespace formic {

  //class InputBase;

  ///////////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief  The base class for Monte Carlo walkers.
  ///
  /// Walkers know how to propose moves, accept/reject them, accumulate data, excecute moves,
  /// and perform population control.
  ///
  /// Not all walkers will need to do all of these tasks, so in some cases a child of this class
  /// will leave one or more of the related functions as an empty function.
  ///
  ///////////////////////////////////////////////////////////////////////////////////////////////////
  class Walker {

    public:

      ///////////////////////////////////////////////////////////////////////////////////////////////////
      /// \brief Virtual destructor so that base pointers destruct children upon deletion
      ///
      ///////////////////////////////////////////////////////////////////////////////////////////////////
      virtual ~Walker() {}

      ///////////////////////////////////////////////////////////////////////////////////////////////////
      /// \brief Proposes a move to a new configuration, which is assumed to be stored internally.
      ///
      /// \param[in]      userinp  the user's input options
      ///
      ///////////////////////////////////////////////////////////////////////////////////////////////////
      virtual void propose_move(const formic::InputBase & userinp) = 0;

      ///////////////////////////////////////////////////////////////////////////////////////////////////
      /// \brief Accumulate data at the current configuration.
      ///
      /// \param[in]      userinp  the user's input options
      ///
      ///////////////////////////////////////////////////////////////////////////////////////////////////
      virtual void accumulate(const formic::InputBase & userinp) = 0;

      ///////////////////////////////////////////////////////////////////////////////////////////////////
      /// \brief Move the walker according to the proposed move and whether it was accepted.
      ///
      /// \param[in]      userinp  the user's input options
      ///
      ///////////////////////////////////////////////////////////////////////////////////////////////////
      virtual void move(const formic::InputBase & userinp) = 0;

      ///////////////////////////////////////////////////////////////////////////////////////////////////
      /// \brief Perform any remaining accumulations and communicate the accumulators' results.
      ///
      /// \param[in]      userinp  the user's input options
      ///
      ///////////////////////////////////////////////////////////////////////////////////////////////////
      virtual void final_accumulate(const formic::InputBase & userinp) = 0;

      ///////////////////////////////////////////////////////////////////////////////////////////////////
      /// \brief reset the walker in preparation for sampling
      ///
      /// \param[in]      userinp  the user's input options
      ///
      ///////////////////////////////////////////////////////////////////////////////////////////////////
      virtual void reset(const formic::InputBase & userinp, const bool reset_co = true) = 0;

  };

  ///////////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief  Sample from a walker's distribution for a given number of iterations.
  ///
  ///////////////////////////////////////////////////////////////////////////////////////////////////
  inline void sample(const formic::InputBase & userinp, const int nsamples, formic::Walker & w, const bool reset_co = true) {

    const int mps = userinp.get<int>("moves_per_sample");

    w.reset(userinp, reset_co);

    //std::srand(std::time(NULL));

    //formic::of << "taking " << nsamples << " samples" << std::endl;
    for (int i = 0; i < nsamples; i++) {

      w.propose_move(userinp);

      w.accumulate(userinp);

      w.move(userinp);

      // make additional move attempts between samples as requested by the user
      const int extra_moves = mps - 1;
      //const int extra_moves = ( mps < 2 ? 0 : 1 + std::rand() % (2*mps-1) );
      for (int j = 0; j < extra_moves; j++) {
        w.propose_move(userinp);
        w.move(userinp);
      }

    }

    w.final_accumulate(userinp);

  }

}

#endif
